//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Linq;

using NHibernate.Criterion;
using NHibernate.Persister.Entity;
using NHibernate.Transform;
using ProjectBase.Core;
using ProjectBase.Data;
using ProjectBase.Data.NH;

using $prjsn$.Entity;
using $prjsn$.Common;

namespace $safeprojectname$
{
    public abstract class BusinessObjectDao<TEntity, TFilter> : BaseDao<TEntity, int, TFilter>
        where TEntity : BusinessObject, new()
        where TFilter : BusinessObjectFilter
    {
        #region IApplyFilterToCriteria

        protected override IList<ICriterion> Apply(TFilter filter)
        {
            var restrictions = base.Apply(filter);

            // Equal
            restrictions.AddCompare(filter, f => f.IdExact); // Searches for exact ID
            restrictions.AddIn(filter, f => f.IDColl);

            // IAuditable
            restrictions.AddIn(filter, f => f.DateCreated);
            restrictions.AddCompare(filter, f => f.DateCreatedFrom);
            restrictions.AddCompare(filter, f => f.DateCreatedTo);

            restrictions.AddIn(filter, f => f.DateModified);
            restrictions.AddCompare(filter, f => f.DateModifiedFrom);
            restrictions.AddCompare(filter, f => f.DateModifiedTo);

            // person Id in
            restrictions.AddIn(filter, f => f.CreatedById);
            restrictions.AddIn(filter, f => f.ModifiedById);

            // IHaveActiveMode
            restrictions.AddIn(filter, f => f.IsActive);

            restrictions.AddIn(filter, f => f.IsVisible);

            // IHaveCountry
            restrictions.AddLike(filter, f => f.CountryCode);

            return restrictions;
        }

        #endregion IApplyFilterToCriteria

        #region override Add, Update

        public override TEntity Add(TEntity entity)
        {
            var result = base.Add(entity);

            var additional = entity.Additional;

            additional[Str.Common.ID] = result.ID;

            Session.Save(EntityName + "Additional", additional);

            return result;
        }

        public override TEntity Update(TEntity entity)
        {
            var result = base.Update(entity);

            var additional = entity.Additional;

            Session.Update(EntityName + "Additional", additional);

            return result;
        }

        public override void Delete(TEntity entity)
        {
            var additional = entity.Additional;

            Session.Delete(EntityName + "Additional", additional);

            base.Delete(entity);
        }

        #endregion override Add, Update

        #region MappingInfo

        private IList<MappingDescription> _extendedMappingInfo;

        protected virtual IList<MappingDescription> ReadAdditionalMappingInfo()
        {
            var mapping = base.MappingInfo;
            var additionalMap = mapping.FirstOrDefault(m => m.PropertyName.Equals("Additional"));

            if (additionalMap.IsNull())
            {
                return mapping;
            }

            //var entityType = typeof(TEntity);
            var factory = NHSessionFactory.GetFactory(SessionFactoryName);
            var persister = factory.GetClassMetadata(EntityName + "Additional") as AbstractEntityPersister;

            if (persister.IsNull())
            {
                throw new InvalidCastException("NHibernate ISessionFactory did not return 'AbstractEntityPersister'  on GetClassMetadata()");
            }

            var propertyNameList = persister.PropertyNames;

            var map = new List<IMappingDescription>();

            foreach (var propertyName in propertyNameList)
            {
                var index = persister.GetPropertyIndex(propertyName);
                var type = persister.GetPropertyType(propertyName);

                var description = new DynamicDescription
                {
                    PropertyName = propertyName,
                    PersistenceName = persister.GetPropertyColumnNames(propertyName).First(),
                    PropertyType = type.ReturnedClass,
                    CanInsert = persister.PropertyInsertability[index],
                    CanUpdate = persister.PropertyUpdateability[index],
                };

                map.Add(description);
            }

            additionalMap.IsDynamic = true;
            additionalMap.DynamicComponents = map;

            return mapping;
        }

        public override IList<MappingDescription> MappingInfo
        {
            get
            {
                var mappingInfo = _extendedMappingInfo;
                if (mappingInfo.IsNull())
                {
                    mappingInfo = ReadAdditionalMappingInfo();
                    _extendedMappingInfo = mappingInfo;
                }
                return mappingInfo;
            }
        }

        #endregion MappingInfo

        #region properties

        protected static readonly string EntityName = typeof(TEntity).Name;

        private static readonly IResultTransformer AdditionalTransformer = new AdditionalTransformer<TEntity>();

        protected override IResultTransformer Transformer
        {
            get { return AdditionalTransformer; }
        }

        public virtual AdditionalFilterProcessor<TEntity, TFilter> AdditionalProcessor { protected get; set; }

        #endregion properties
    }
}